using System;
using System.Globalization;
using Microsoft.CSharp;

namespace Sixeyed.CodeGeneration.Generation
{
    /// <summary>
    /// Provides valid .NET member names
    /// </summary>
    public static class MemberName
    {
        #region Private static members

        private static CSharpCodeProvider _codeProvider;

        private static CSharpCodeProvider CodeProvider
        {
            get
            {
                if (_codeProvider == null)
                {
                    _codeProvider = new CSharpCodeProvider();
                }
                return _codeProvider;
            }
        }

        #endregion

        #region Public static methods

        /// <summary>
        /// Returns the given identifier with invalid characters replaced
        /// </summary>
        /// <param name="identifier">Original identifier</param>
        /// <returns>Valid identifier</returns>
        public static string GetValidIdentifier(string identifier)
        {
            string result = null;
            if (!CodeProvider.IsValidIdentifier(result))
            {
                //CreateValidIdentifier is a good name, but the method doesn't seem to work:
                //result = CodeProvider.CreateValidIdentifier(idName);
                UnicodeCategory category;
                int index = 0;
                result = identifier.Replace(" ", string.Empty).Replace("@", string.Empty); ;
                index = 1;
                while (index < result.Length)
                {
                    category = Char.GetUnicodeCategory(result[index]);
                    if (category != UnicodeCategory.UppercaseLetter &&
                          UnicodeCategory.LowercaseLetter != category &&
                          UnicodeCategory.TitlecaseLetter != category &&
                          UnicodeCategory.ModifierLetter != category &&
                          UnicodeCategory.OtherLetter != category &&
                          UnicodeCategory.LetterNumber != category &&
                          UnicodeCategory.NonSpacingMark != category &&
                          UnicodeCategory.SpacingCombiningMark != category &&
                          UnicodeCategory.DecimalDigitNumber != category &&
                          UnicodeCategory.ConnectorPunctuation != category)
                    {
                        result = result.Replace(result[index], '_');
                    }
                    index++;
                }

                //error if not valid:
                if (!CodeProvider.IsValidIdentifier(result))
                {
                    throw new ApplicationException(string.Format("Invalid identifier: {0}, could not make valid: {1}", identifier, result));
                }
            }
            return result;
        }

        /// <summary>
        /// Returns camel-cased version of the given identifier
        /// </summary>
        /// <param name="identifier">Original identifier</param>
        /// <returns>Camel-cased identifier</returns>
        public static string ToCamelCase(string identifier)
        {
            //TODO - make this more spohisticated, e.g. RETURN_VALUE -> returnValue !rETURN_VALUE
            return string.Format("{0}{1}", identifier.Substring(0, 1).ToLower(), identifier.Substring(1));
        }

        /// <summary>
        /// Returns camel-cased version of the given identifier
        /// </summary>
        /// <param name="identifier">Original identifier</param>
        /// <param name="makeValid">Whether to replace invalid characters</param>
        /// <returns>Camel-cased identifier</returns>
        public static string ToCamelCase(string identifier, bool makeValid)
        {
            if (makeValid)
            {
                identifier = GetValidIdentifier(identifier);
            }
            return ToCamelCase(identifier);
        }

        /// <summary>
        /// Returns Pascal-cased version of the given identifier
        /// </summary>
        /// <param name="identifier">Original identifier</param>
        /// <returns>Pascal-cased identifier</returns>
        public static string ToPascalCase(string identifier)
        {
            //TODO - make this more spohisticated, e.g. RETURN_VALUE -> ReturnValue !RETURN_VALUE
            return string.Format("{0}{1}", identifier.Substring(0, 1).ToUpper(), identifier.Substring(1));
        }

        /// <summary>
        /// Returns Pascal-cased version of the given identifier
        /// </summary>
        /// <param name="identifier">Original identifier</param>
        /// <param name="makeValid">Whether to replace invalid characters</param>
        /// <returns>Pascal-cased identifier</returns>
        public static string ToPascalCase(string identifier, bool makeValid)
        {
            if (makeValid)
            {
                identifier = GetValidIdentifier(identifier);
            }
            return ToPascalCase(identifier);
        }

        /// <summary>
        /// Returns a valid field name
        /// </summary>
        /// <remarks>
        /// Uses camel case with a leading underscore
        /// </remarks>
        /// <param name="identifier">Original identifier</param>
        /// <returns>Valid field name</returns>
        public static string GetFieldName(string identifier)
        {
            identifier = identifier.TrimStart("_".ToCharArray());
            identifier = ToCamelCase(identifier, true);
            identifier = string.Concat("_", identifier);
            return GetValidIdentifier(identifier);
        }

        /// <summary>
        /// Returns a valid property name
        /// </summary>
        /// <remarks>
        /// Uses Pascal case
        /// </remarks>
        /// <param name="identifier">Original identifier</param>
        /// <returns>Valid property name</returns>
        public static string GetPropertyName(string identifier)
        {
            return GetValidIdentifier(ToPascalCase(identifier));
        }

        #endregion
    }
}
